﻿using mozhi.smarterp.Finance.Accounts;
using mozhi.smarterp.Finance.Accounts.Repository;
using mozhi.smarterp.Finance.Accounts.Services;
using mozhi.smarterp.Finance.Banks.Repository;
using mozhi.smarterp.Finance.Enums;
using mozhi.smarterp.Finance.Finances.Repository;
using mozhi.smarterp.Finance.Finances.ValueObjects;
using mozhi.smarterp.Finance.Querys;
using Mozhi.Abp.Core.Extensions;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Services;
using Volo.Abp.Guids;

namespace mozhi.smarterp.Finance.Finances.Services
{
    public class SettlementMananger : DomainService
    {
        private IAccountBalanceRepository _balanceRepository;
        private IJournalRepository _journalRepository;
        private ISettlementRepository _repository;
        private IOrganizationRepository _organizationRepository;
        private IAccountRepository _accountRepository;
        private IGuidGenerator _guidGenerator;
        private AccountBalanceManager _balanceManager;
        private IBankAccountRepository _bankAccountRepository;

        public SettlementMananger(ISettlementRepository repository , IAccountBalanceRepository accountBalanceRepository , IJournalRepository journalRepository,
            IOrganizationRepository organizationRepository, IAccountRepository accountRepository , IGuidGenerator guidGenerator , 
            AccountBalanceManager balanceManager , IBankAccountRepository bankAccountRepository)
        {
            _repository = repository;
            _balanceRepository = accountBalanceRepository;
            _journalRepository = journalRepository;
            _organizationRepository = organizationRepository;
            _accountRepository = accountRepository;
            _guidGenerator = guidGenerator;
            _balanceManager = balanceManager;
            _bankAccountRepository = bankAccountRepository;
        }

        public async Task<Tuple<Settlement , List<AccountBalance>>> ClosePeriod(int period)
        {
            //初始化期间
            var initalizePeriod = await GetInitalizePeriodAsync();

            if (period == initalizePeriod)
            {
                //检查是否试算平衡
                var trialResult = await GetTrialBalanceAsync();

                if (!trialResult.Passed)
                    throw new UserFriendlyException("试算不平衡，无法结账","100000");
            }

            var settlements = await _repository.FindAsync(new SettlementQuery() { Periods = new List<int>() { period.ToPrePeriod(), period } });

            var currentPeriodSettlement = settlements.FirstOrDefault(x => x.Period == period);

            currentPeriodSettlement = currentPeriodSettlement == null ? new Settlement(_guidGenerator.Create(), period) : currentPeriodSettlement;

            var balances = new List<AccountBalance>();

            if (period == initalizePeriod)
            {
                if (currentPeriodSettlement != null && currentPeriodSettlement.Closed == true)
                    throw new UserFriendlyException("初始话已完成", "100000");

                currentPeriodSettlement.Close(initalizePeriod, true);

                //需要将期初的余额放到下一期
                balances = await GetAccountBalancesAsync(period, true);

                return new Tuple<Settlement, List<AccountBalance>>(currentPeriodSettlement, balances);
            }

            var prePeriodSettlement = settlements.FirstOrDefault(x => x.Period == period.ToPrePeriod());

            //期间是否已经结账
            if (currentPeriodSettlement != null && currentPeriodSettlement.Closed == true)
                throw new UserFriendlyException($"期间：{currentPeriodSettlement.Period}已结账", "100000");

            currentPeriodSettlement.Close(period, prePeriodSettlement != null && prePeriodSettlement.Closed == true);

            //期间是否存在未审核凭证
            var unApprovedJournalCount = await _journalRepository.GetCountAsync(new JournalPageQuery() { PageIndex = 1, PageSize = int.MaxValue, Approved = false, Period = period });

            if (unApprovedJournalCount > 0)
                throw new UserFriendlyException($"期间：{currentPeriodSettlement.Period}存在未审核凭证", "100000");

            //期间凭证是否断号
            var journalNumberKeyValues = await _journalRepository.FindNumberKeyvaluesAsync(period);

            var isInterruption = false;

            foreach (var journalNumberKeyValue in journalNumberKeyValues)
            {
                var numbers = journalNumberKeyValue.Value;

                var maxNumber = numbers.Max(x => int.Parse(x));

                isInterruption = numbers.Count != maxNumber;

                if (isInterruption)
                    break;

            }
            if (isInterruption)
                throw new UserFriendlyException($"期间：{currentPeriodSettlement.Period}凭证编号存在中断", "100000");

            balances = await GetAccountBalancesAsync(period, false);

            return new Tuple<Settlement, List<AccountBalance>>(currentPeriodSettlement, balances);
        }

        public async Task<Tuple<Settlement, List<AccountBalance>>> OpenPeriod(int period)
        {
            var initalizePeriod = await GetInitalizePeriodAsync();

            var settlements = await _repository.FindAsync(new SettlementQuery() { Periods = new List<int>() { period, period.ToNextPeriod() } });

            var balances = new List<AccountBalance>();

            var currentPeriodSettlement = settlements.FirstOrDefault(x => x.Period == period);

            if (currentPeriodSettlement == null || currentPeriodSettlement.Closed == false)
            {
                var tips = initalizePeriod == period ? "当前账套还未完成初始化" : $"期间：{period}未结账";

                throw new UserFriendlyException(tips, "100000");
            }

            var nextPeriodSettlement = settlements.FirstOrDefault(x => x.Period == period.ToNextPeriod());

            currentPeriodSettlement.Open(initalizePeriod, nextPeriodSettlement != null && nextPeriodSettlement.Closed);

            //如果期间是初始化期间，需要查询下一期的科目余额，将其清除掉
            if (period == initalizePeriod)
                balances = await _balanceRepository.FindAsync(new AccountBalanceQuery() { Period = period.ToNextPeriod() });

            return new Tuple<Settlement, List<AccountBalance>>(currentPeriodSettlement, balances);
        }


        public async Task<TrialBalanceResult> GetTrialBalanceAsync()
        {
            //var result = new TrialBalanceResult();

            //var initalizePeriod = await GetInitalizePeriodAsync();

            //var accounts = await _accountRepository.FindAsync(new AccountQuery() { OnlyFirstLevel = true });

            //var accountIds = accounts.Select(x => x.Id).ToList();

            //var query = new AccountBalanceQuery() { AccountIds = accountIds, Period = initalizePeriod, IncludeDimension = false, IncludeCurrency = false };

            //var balances = await _balanceRepository.FindAsync(query);

            //if (balances.Count == 0)
            //{
            //    result.Passed = true;
            //    return result;
            //}

            //result.DebitOpeningAmount = balances.Where(x => x.Direction == AccountDirectionType.Debit && x.OpeningAmount.HasValue)
            //    .Sum(x => x.OpeningAmount.Value);

            //result.CreditOpeningAmount = balances.Where(x => x.Direction == AccountDirectionType.Credit && x.OpeningAmount.HasValue)
            //    .Sum(x => x.OpeningAmount.Value);

            //result.CurrentYearDebitAmount = balances.Where(x => x.CurrentYearDebitAmount.HasValue)
            //    .Sum(x => x.CurrentYearDebitAmount.Value);

            //result.CurrentYearCreditAmount = balances.Where(x => x.CurrentYearCreditAmount.HasValue)
            //    .Sum(x => x.CurrentYearCreditAmount.Value);

            //result.Passed = result.DebitOpeningAmount == result.CreditOpeningAmount && result.CurrentYearDebitAmount == result.CurrentYearCreditAmount;

            //return result;
            throw new NotImplementedException();

        }

        public async Task<int> GetInitalizePeriodAsync()
        {
            var organization = await _organizationRepository.GetAsync();

            organization.ValidateFinance();

            var initalizePeriod = organization.ActivatedDate.Value.ToPeriod().ToPrePeriod();

            return initalizePeriod;
        }

        #region
        private async Task<List<AccountBalance>> GetAccountBalancesAsync(int period, bool isInitalizePeriod)
        {
            List<AccountBalance> result = new List<AccountBalance>();

            if (isInitalizePeriod == false)
            {
                result = await _balanceManager.GetUnClosePeriodBalances(period);

                return result;
            }

            var organization = await _organizationRepository.GetAsync();

            //如果是初始化期间，需要将初始化期间带入下一期中
            var initalizeBalances = await _balanceRepository.FindAsync(new AccountBalanceQuery { Period = period });

            result = await _balanceManager.CalculateCurrentPeriodBalances(initalizeBalances, period.ToNextPeriod(), false , organization);

            return result;

        }
        #endregion
    }
}
